#region The MIT License
// The MIT License
// 
// Copyright (c) 2009 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;
using System.IO;
using System.Reflection;

using NUnit.Framework;

using octalforty.Componento.ComponentModel.Installation;
using octalforty.Componento.ComponentModel.Installation.FileSystem;
using octalforty.Componento.ComponentModel.Tracing;
using octalforty.Componento.Shared.SemanticModel;
using Rhino.Mocks;

namespace octalforty.Componento.Tests.Componento.ComponentModel.Installation
{
    [TestFixture()]
    public class ComponentInstallerServiceTestFixture
    {
        [Test()]
        [ExpectedException(typeof(ComponentInstallationException))]
        public void CannotInstallTwoVersionsOfSameComponent()
        {
            var mockRepository = new MockRepository();

            var componentManifestProvider = mockRepository.StrictMock<IComponentManifestProviderService>();
            var installedComponentRegistry = mockRepository.StrictMock<IInstalledComponentRegistryService>();

            var repositoryUri = new Uri("svn+http://foo/nhibernate");
            var requirement =
                VersionSpecification.GreaterOrEqual(new Version(2, 1, 2));

            using(mockRepository.Record())
            {
                Expect.Call(componentManifestProvider.GetComponentManifest("nhibernate", repositoryUri, requirement)).
                    Return(new ComponentManifest("nhibernate", new Version(2, 1, 2)));

                Expect.Call(installedComponentRegistry.GetComponentReference("nhibernate")).
                    Return(new ComponentReference("nhibernate", new Version(2, 5), repositoryUri));
            } // using

            var componentInstaller = new ComponentInstallerService(new TraceManagerService())
                {
                    ComponentManifestProvider = componentManifestProvider,
                    InstalledComponentRegistry = installedComponentRegistry
                };

            componentInstaller.Install("nhibernate", repositoryUri, requirement);
        }

        [Test()]
        public void InstallComponentWithoutDependencies()
        {
            var mockRepository = new MockRepository();

            var componentManifestProvider = mockRepository.StrictMock<IComponentManifestProviderService>();
            var installedComponentRegistry = mockRepository.StrictMock<IInstalledComponentRegistryService>();
            var resourceProvider = mockRepository.StrictMock<IResourceProviderService>();
            var resourceInstaller = mockRepository.StrictMock<IResourceInstallerService>();
            var componentDependenciesProvider =
                mockRepository.StrictMock<IComponentDependenciesProviderService>();
            var fileSystemManager = mockRepository.StrictMock<IFileSystemManagerService>();

            var repositoryUri = new Uri("svn+http://foo/nhibernate");
            var requirement =
                VersionSpecification.GreaterOrEqual(new Version(2, 1, 2));
            var manifest = new ComponentManifest("nhibernate", new Version(2, 1, 2));
            var resources = new Uri[] { new Uri("svn+http://foo/nhibernate/trunk") };

            using(mockRepository.Ordered())
            {
                Expect.Call(componentManifestProvider.GetComponentManifest("nhibernate", repositoryUri, requirement)).
                    Return(manifest);

                Expect.Call(installedComponentRegistry.GetComponentReference("nhibernate")).
                    Return(null);
                
                Expect.Call(resourceProvider.GetResources(manifest, requirement)).
                    Return(resources);

                Expect.Call(fileSystemManager.CurrentDirectory).
                    Return(@"d:\lib");
                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(false);
                Expect.Call(() => fileSystemManager.CreateDirectory(@"d:\lib\nhibernate"));

                Expect.Call(() => resourceInstaller.InstallResources(resources, @"d:\lib\nhibernate", null));

                Expect.Call(
                    delegate 
                        { 
                            installedComponentRegistry.RegisterComponent(
                                new ComponentReference("nhibernate", manifest.Descriptor.Version, repositoryUri)); 
                        });

                Expect.Call(componentDependenciesProvider.GetComponentDependencies(manifest)).
                    Return(new ComponentReference[] { });
            } // using

            mockRepository.ReplayAll();

            var componentInstaller = new ComponentInstallerService(new TraceManagerService())
                {
                    ComponentManifestProvider = componentManifestProvider,
                    InstalledComponentRegistry = installedComponentRegistry,
                    ResourceProvider = resourceProvider,
                    ResourceInstaller = resourceInstaller,
                    ComponentDependenciesProvider = componentDependenciesProvider,
                    FileSystemManager = fileSystemManager
                };

            componentInstaller.Install("nhibernate", repositoryUri, requirement);
        }

        [Test()]
        public void InstallComponentWithDependencies()
        {
            var mockRepository = new MockRepository();

            var componentManifestProvider = mockRepository.StrictMock<IComponentManifestProviderService>();
            var installedComponentRegistry = mockRepository.StrictMock<IInstalledComponentRegistryService>();
            var resourceProvider = mockRepository.StrictMock<IResourceProviderService>();
            var resourceInstaller = mockRepository.StrictMock<IResourceInstallerService>();
            var componentDependenciesProvider =
                mockRepository.StrictMock<IComponentDependenciesProviderService>();
            var fileSystemManager = mockRepository.StrictMock<IFileSystemManagerService>();

            var nhibernateRepositoryUri = new Uri("svn+http://foo/nhibernate");
            var nunitRepositoryUri = new Uri("http://nunit");
            var nhibernateRequirement =
                VersionSpecification.GreaterOrEqual(new Version(2, 1, 2));
            var nunitRequirement =
                VersionSpecification.GreaterOrEqual(new Version(0, 0, 0, 0));
            var nhibernateManifest = new ComponentManifest("nhibernate", new Version(2, 1, 2));
            var nunitManifest = new ComponentManifest("nunit", new Version(2, 0));
            var nhibernateResources = new Uri[] { new Uri("svn+http://foo/nhibernate/trunk") };
            var nunitResources = new Uri[] { new Uri("http://nunit/trunk") };

            using(mockRepository.Ordered())
            {
                Expect.Call(componentManifestProvider.GetComponentManifest("nhibernate", nhibernateRepositoryUri, nhibernateRequirement)).
                    Return(nhibernateManifest);

                Expect.Call(installedComponentRegistry.GetComponentReference("nhibernate")).
                    Return(null);
                
                Expect.Call(resourceProvider.GetResources(nhibernateManifest, nhibernateRequirement)).
                    Return(nhibernateResources);

                Expect.Call(fileSystemManager.CurrentDirectory).
                    Return(@"d:\lib");
                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(false);
                Expect.Call(delegate { fileSystemManager.CreateDirectory(@"d:\lib\nhibernate"); });

                Expect.Call(delegate { resourceInstaller.InstallResources(nhibernateResources, @"d:\lib\nhibernate", null); });

                Expect.Call(
                    delegate 
                        { 
                            installedComponentRegistry.RegisterComponent(
                                new ComponentReference("nhibernate", nhibernateManifest.Descriptor.Version, nhibernateRepositoryUri)); 
                        });

                Expect.Call(componentDependenciesProvider.GetComponentDependencies(nhibernateManifest)).
                    Return(new ComponentReference[] { new ComponentReference("nunit", null, nunitRepositoryUri),  });

                Expect.Call(installedComponentRegistry.GetComponentReference("nunit")).
                    Return(null);

                Expect.Call(componentManifestProvider.GetComponentManifest("nunit", nunitRepositoryUri, 
                    VersionSpecification.GreaterOrEqual(new Version(0, 0, 0, 0)))).
                    Return(nunitManifest);

                Expect.Call(installedComponentRegistry.GetComponentReference("nunit")).
                    Return(null);

                Expect.Call(resourceProvider.GetResources(nunitManifest, nunitRequirement)).
                    Return(nunitResources);

                Expect.Call(fileSystemManager.CurrentDirectory).
                    Return(@"d:\lib");
                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nunit")).
                    Return(false);
                Expect.Call(delegate { fileSystemManager.CreateDirectory(@"d:\lib\nunit"); });

                Expect.Call(delegate { resourceInstaller.InstallResources(nunitResources, @"d:\lib\nunit", null); });

                Expect.Call(
                    delegate 
                        { 
                            installedComponentRegistry.RegisterComponent(
                                new ComponentReference("nunit", nunitManifest.Descriptor.Version, nunitRepositoryUri)); 
                        });

                Expect.Call(componentDependenciesProvider.GetComponentDependencies(nunitManifest)).
                    Return(new ComponentReference[] {  });

                Expect.Call(
                    delegate
                        {
                            installedComponentRegistry.RegisterComponentDependencies("nhibernate",
                                new string[] { "nunit" });
                        });
            } // using

            mockRepository.ReplayAll();

            var componentInstaller = new ComponentInstallerService(new TraceManagerService())
                {
                    ComponentManifestProvider = componentManifestProvider,
                    InstalledComponentRegistry = installedComponentRegistry,
                    ResourceProvider = resourceProvider,
                    ResourceInstaller = resourceInstaller,
                    ComponentDependenciesProvider = componentDependenciesProvider,
                    FileSystemManager = fileSystemManager
                };

            componentInstaller.Install("nhibernate", nhibernateRepositoryUri, nhibernateRequirement);
        }

        [Test()]
        public void InstallComponentWithConflictingDependenciesUninstallsPartiallyInstalledComponents()
        {
            var mockRepository = new MockRepository();

            var componentManifestProvider = mockRepository.StrictMock<IComponentManifestProviderService>();
            var resourceProvider = mockRepository.StrictMock<IResourceProviderService>();
            var resourceInstaller = mockRepository.StrictMock<IResourceInstallerService>();
            var componentDependenciesProvider =
                mockRepository.StrictMock<IComponentDependenciesProviderService>();
            var fileSystemManager = mockRepository.StrictMock<IFileSystemManagerService>();

            var installedComponentRegistry = new InstalledComponentRegistryService(new TraceManagerService())
                {
                    DatabaseRootDirectory =
                        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                    FileSystemManager = new FileSystemManagerService()
                };

            string databasePath = Path.Combine(installedComponentRegistry.DatabaseRootDirectory, "componento.db");
            if(File.Exists(databasePath))
                File.Delete(databasePath);

            installedComponentRegistry.OnImportsSatisfied();

            Uri nhibernateRepositoryUri = new Uri("svn+http://foo/nhibernate");
            Uri nunitRepositoryUri = new Uri("http://nunit");

            var nhibernateSpecification =
                VersionSpecification.GreaterOrEqual(new Version(2, 1, 2));
            var nunitSpecification =
                VersionSpecification.GreaterOrEqual(new Version(0, 0, 0, 0));

            ComponentManifest nhibernateManifest = new ComponentManifest("nhibernate", new Version(2, 1, 2));
            ComponentManifest nunitManifest = new ComponentManifest("nunit", new Version(2, 0));

            Uri[] nhibernateResources = new Uri[] { new Uri("svn+http://foo/nhibernate/trunk") };
            Uri[] nunitResources = new Uri[] { new Uri("http://nunit/trunk") };

            using(mockRepository.Unordered())
                Expect.Call(fileSystemManager.CurrentDirectory).
                    Return(@"d:\lib").Repeat.Any();

            using(mockRepository.Ordered())
            {
                Expect.Call(componentManifestProvider.GetComponentManifest("nhibernate", nhibernateRepositoryUri, nhibernateSpecification)).
                    Return(nhibernateManifest);

                Expect.Call(resourceProvider.GetResources(nhibernateManifest, nhibernateSpecification)).
                    Return(nhibernateResources);

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(false);
                Expect.Call(delegate { fileSystemManager.CreateDirectory(@"d:\lib\nhibernate"); });

                Expect.Call(delegate { resourceInstaller.InstallResources(nhibernateResources, @"d:\lib\nhibernate", null); });

                Expect.Call(componentDependenciesProvider.GetComponentDependencies(nhibernateManifest)).
                    Return(new ComponentReference[] { new ComponentReference("nunit", null, nunitRepositoryUri),  });

                Expect.Call(componentManifestProvider.GetComponentManifest("nunit", nunitRepositoryUri,
                    VersionSpecification.GreaterOrEqual(new Version(0, 0, 0, 0)))).
                    Return(nunitManifest);

                Expect.Call(resourceProvider.GetResources(nunitManifest, nunitSpecification)).
                    Return(nunitResources);

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nunit")).
                    Return(false);
                Expect.Call(delegate { fileSystemManager.CreateDirectory(@"d:\lib\nunit"); });

                Expect.Call(delegate { resourceInstaller.InstallResources(nunitResources, @"d:\lib\nunit", null); });

                ComponentReference nhibernate3Reference = new ComponentReference("nhibernate", new Version(3, 0), new Uri("http://nh3"));
                Expect.Call(componentDependenciesProvider.GetComponentDependencies(nunitManifest)).
                    Return(new ComponentReference[] { nhibernate3Reference });

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nunit")).
                    Return(true);

                Expect.Call(delegate { fileSystemManager.DeleteDirectory(@"d:\lib\nunit"); });

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(true);

                Expect.Call(delegate { fileSystemManager.DeleteDirectory(@"d:\lib\nhibernate"); });
            } // using

            mockRepository.ReplayAll();

            ComponentInstallerService componentInstaller = new ComponentInstallerService(new TraceManagerService());
            componentInstaller.ComponentManifestProvider = componentManifestProvider;
            componentInstaller.InstalledComponentRegistry = installedComponentRegistry;
            componentInstaller.ResourceProvider = resourceProvider;
            componentInstaller.ResourceInstaller = resourceInstaller;
            componentInstaller.ComponentDependenciesProvider = componentDependenciesProvider;
            componentInstaller.FileSystemManager = fileSystemManager;

            try
            {
                componentInstaller.Install("nhibernate", nhibernateRepositoryUri, nhibernateSpecification);
                Assert.Fail();
            }
            catch(ComponentInstallationException e)
            {
            }
            catch(Exception e)
            {
                Assert.Fail(e.ToString());
            }

            Assert.IsEmpty(installedComponentRegistry.InstalledComponents);
        }

        [Test()]
        public void InstallComponentWithMissingDependencyUninstallsPartiallyInstalledComponents()
        {
            var mockRepository = new MockRepository();

            var componentManifestProvider = mockRepository.StrictMock<IComponentManifestProviderService>();
            var resourceProvider = mockRepository.StrictMock<IResourceProviderService>();
            var resourceInstaller = mockRepository.StrictMock<IResourceInstallerService>();
            var componentDependenciesProvider =
                mockRepository.StrictMock<IComponentDependenciesProviderService>();
            var fileSystemManager = mockRepository.StrictMock<IFileSystemManagerService>();

            var installedComponentRegistry = new InstalledComponentRegistryService(new TraceManagerService())
                {
                    DatabaseRootDirectory =
                        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                    FileSystemManager = new FileSystemManagerService()
                };

            string databasePath = Path.Combine(installedComponentRegistry.DatabaseRootDirectory, "componento.db");
            if(File.Exists(databasePath))
                File.Delete(databasePath);

            installedComponentRegistry.OnImportsSatisfied();

            var nhibernateRepositoryUri = new Uri("svn+http://foo/nhibernate");
            var nunitRepositoryUri = new Uri("http://nunit");

            var nhibernateRequirement =
                VersionSpecification.GreaterOrEqual(new Version(2, 1, 2));

            var nhibernateManifest = new ComponentManifest("nhibernate", new Version(2, 1, 2));

            var nhibernateResources = new Uri[] { new Uri("svn+http://foo/nhibernate/trunk") };

            using(mockRepository.Unordered())
                Expect.Call(fileSystemManager.CurrentDirectory).
                    Return(@"d:\lib").Repeat.Any();

            using(mockRepository.Ordered())
            {
                Expect.Call(componentManifestProvider.GetComponentManifest("nhibernate", nhibernateRepositoryUri, nhibernateRequirement)).
                    Return(nhibernateManifest);

                Expect.Call(resourceProvider.GetResources(nhibernateManifest, nhibernateRequirement)).
                    Return(nhibernateResources);

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(false);
                Expect.Call(() => fileSystemManager.CreateDirectory(@"d:\lib\nhibernate"));

                Expect.Call(() => resourceInstaller.InstallResources(nhibernateResources, @"d:\lib\nhibernate", null));

                Expect.Call(componentDependenciesProvider.GetComponentDependencies(nhibernateManifest)).
                    Return(new ComponentReference[] { new ComponentReference("nunit", null, nunitRepositoryUri),  });

                Expect.Call(componentManifestProvider.GetComponentManifest("nunit", nunitRepositoryUri,
                    VersionSpecification.GreaterOrEqual(new Version(0, 0, 0, 0)))).
                    Return(null);

                Expect.Call(fileSystemManager.DirectoryExists(@"d:\lib\nhibernate")).
                    Return(true);

                Expect.Call(delegate { fileSystemManager.DeleteDirectory(@"d:\lib\nhibernate"); });
            } // using

            mockRepository.ReplayAll();

            ComponentInstallerService componentInstaller = new ComponentInstallerService(new TraceManagerService());
            componentInstaller.ComponentManifestProvider = componentManifestProvider;
            componentInstaller.InstalledComponentRegistry = installedComponentRegistry;
            componentInstaller.ResourceProvider = resourceProvider;
            componentInstaller.ResourceInstaller = resourceInstaller;
            componentInstaller.ComponentDependenciesProvider = componentDependenciesProvider;
            componentInstaller.FileSystemManager = fileSystemManager;

            try
            {
                componentInstaller.Install("nhibernate", nhibernateRepositoryUri, nhibernateRequirement);
                Assert.Fail();
            }
            catch(ComponentInstallationException e)
            {
            }
            catch(Exception e)
            {
                Assert.Fail(e.ToString());
            }

            Assert.IsEmpty(installedComponentRegistry.InstalledComponents);
        }
    }
}
